<?php
/***********************************************************************
 * @package    	IvanEngine
 * @subpackage 	Core
 * @author     	Ivan <ivanzx@msn.com>
 * @created    	2010-05-17
 ***********************************************************************/
if(defined("IVANENGINE_CORE_VALIDATOR_DATE"))
	die("Redefinition macro in :".__FILE__."(".__LINE__.")");
define("IVANENGINE_CORE_VALIDATOR_DATE", true);

require_once (dirname(__FILE__).'/ValidatorBase.php');

class CValidatorDate extends CValidatorBase 
{
	const NM_BadFormat		= 'NM_BadFormat';
	const NM_Max			= 'NM_Max';
	const NM_Min			= 'NM_Min';
	const NM_CantToDate		= 'NM_CantToDate';
	
	const NO_DateFormat		= 'NO_DateFormat';
	const NO_WithTime		= 'NO_WithTime';
	const NO_DateOutput		= 'NO_DateOutput';
	const NO_DateTimeOutput	= 'NO_DateTimeOutput';
	const NO_DateFormatError= 'NO_DateFormatError';
	const NO_Min			= 'NO_Min';
	const NO_Max			= 'NO_Max';
	const NO_DateFormatRangeError	= 'NO_DateFormatRangeError';
	protected function Initialize($options = array(), $messages = array())
  	{
    	$this->AddMessage(self::NM_BadFormat, '"%value%" does not match the date format (%'.NO_DateFormat.'%).');
	    $this->AddMessage(self::NM_Max, 'The date must be before %max%.');
	    $this->AddMessage(self::NM_Min, 'The date must be after %min%.');
	    $this->AddMessage(self::NM_CantToDate, '"%value%" can not convert to time date.');
	
	    $this->AddOption(self::NO_DateFormat, null);
	    $this->AddOption(self::NO_WithTime, false);
	    $this->AddOption(self::NO_DateOutput, 'Y-m-d');
	    $this->AddOption(self::NO_DateTimeOutput, 'Y-m-d H:i:s');
	    $this->AddOption(self::NO_DateFormatError);
	    $this->AddOption(self::NO_Min, null);
	    $this->AddOption(self::NO_Max, null);
	    $this->AddOption(self::NO_DateFormatRangeError, 'd/m/Y H:i:s');
  	}

  	protected function DoVerify($value)
  	{
  		$regex	= $this->GetOption(self::NO_DateFormat);
    	if (is_array($value))
    	{
      		$clean = $this->ConvertDateArrayToTimestamp($value);
    	}
    	else if ($regex)
    	{
    		$match	= array();
      		if (!preg_match($regex, $value, $match))
      		{
        		throw new CExceptionValidator($this, self::NM_BadFormat, 
        		array('value' => $value, self::NO_DateFormat => 
        			$this->GetOption(self::NO_DateFormatError) ? 
        			$this->GetOption(self::NO_DateFormatError) : 
        			$this->GetOption(self::NO_DateFormat)));
      		}

      		$clean = $this->ConvertDateArrayToTimestamp($match);
    	}
    	else if (!ctype_digit($value))
    	{
      		$clean = strtotime($value);
      		if (false === $clean)
      		{
        		throw new CExceptionValidator($this, self::NM_CantToDate, array('value' => $value));
      		}
	    }
	    else
	    {
	      	$clean = (integer) $value;
	    }

	    if ($this->HasOption(self::NO_Max) && $clean > $this->GetOption('max'))
	    {
	      	throw new CExceptionValidator($this, self::NM_Max, 
	      		array(	'value' => $value, 
	      				'NO_Max' => date($this->GetOption(self::NO_DateFormatRangeError), $this->GetOption(self::NO_Max))));
	    }
	
	    if ($this->HasOption(self::NO_Min) && $clean < $this->GetOption(self::NO_Min))
	    {
	      	throw new CExceptionValidator($this, self::NO_Min, 
	      		array(	'value' => $value, 
	      				'NO_Min' => date($this->GetOption(self::NO_DateFormatRangeError), $this->GetOption(self::NO_Min))));
	    }
	
	    return $clean === $this->GetEmptyValue() ? $clean : date($this->GetOption(self::NO_WithTime) ? $this->GetOption(self::NO_DateTimeOutput) : $this->GetOption(self::NO_DateOutput), $clean);
  	}
  	
	protected function ConvertDateArrayToTimestamp($value)
  	{
    	// all elements must be empty or a number
    	foreach (array('year', 'month', 'day', 'hour', 'minute', 'second') as $key)
    	{
      		if (isset($value[$key]) && !preg_match('#^\d+$#', $value[$key]) && !empty($value[$key]))
      		{
        		throw new CExceptionValidator($this, self::NM_Invalid, array('value' => $value));
      		}
    	}

    	// if one date value is empty, all others must be empty too
    	$empties =
      	(!isset($value['year']) || !$value['year'] ? 1 : 0) +
      	(!isset($value['month']) || !$value['month'] ? 1 : 0) +
      	(!isset($value['day']) || !$value['day'] ? 1 : 0)
    	;
    	if ($empties > 0 && $empties < 3)
    	{
      		throw new CExceptionValidator($this, self::NM_Invalid, array('value' => $value));
    	}
    	else if (3 == $empties)
    	{
      		return $this->GetEmptyValue();
    	}

    	if (!checkdate(intval($value['month']), intval($value['day']), intval($value['year'])))
    	{
      		throw new CExceptionValidator($this, self::NM_Invalid, array('value' => $value));
    	}

    	if ($this->GetOption(self::NO_WithTime))
    	{
      		// if second is set, minute and hour must be set
      		// if minute is set, hour must be set
      		if (
        	$this->IsValueSet($value, 'second') && (!$this->IsValueSet($value, 'minute') || !$this->IsValueSet($value, 'hour'))
        	||
        	$this->IsValueSet($value, 'minute') && !$this->IsValueSet($value, 'hour')
      		)
      		{
        		throw new CExceptionValidator($this, self::NM_Invalid, array('value' => $value));
      		}

      		$clean = mktime(
	        isset($value['hour']) ? intval($value['hour']) : 0,
	        isset($value['minute']) ? intval($value['minute']) : 0,
	        isset($value['second']) ? intval($value['second']) : 0,
	        intval($value['month']),
	        intval($value['day']),
	        intval($value['year'])
      		);
	    }
	    else
	    {
	      	$clean = mktime(0, 0, 0, intval($value['month']), intval($value['day']), intval($value['year']));
	    }
	
	    if (false === $clean)
	    {
	      	throw new CExceptionValidator($this, self::NM_Invalid, array('value' => var_export($value, true)));
	    }
	
	    return $clean;
  	}

  	protected function IsValueSet($values, $key)
  	{
    	return isset($values[$key]) && !in_array($values[$key], array(null, ''), true);
  	}

  	protected function IsEmpty($value)
  	{
    	if (is_array($value))
    	{
      		$filtered = array_filter($value);

      		return empty($filtered);
    	}

    	return parent::IsEmpty($value);
  	}
}

?>